package org.fin.session;

import org.fin.exception.NoSuchSqlStatementException;
import org.fin.exception.NotSupportedException;
import org.fin.pojo.Configuration;
import org.fin.pojo.MappedStatement;
import org.fin.session.executor.Executor;
import org.fin.session.executor.SimpleExecutor;

import java.lang.reflect.Proxy;
import java.sql.SQLException;
import java.util.Collection;
import java.util.List;
import java.util.Map;

public class DefualtSqlSession implements SqlSession {

  private final Configuration configuration;

  public DefualtSqlSession(Configuration configuration) {
    this.configuration = configuration;
  }




  @Override
  public <R> List<R> selectList(MappedStatement statement, Object... params) throws Exception {
    // 要去完成对SimpleExecutor去执行JDBC的调用
    SimpleExecutor executor = new SimpleExecutor();
    return executor.query(configuration, statement, params);
  }


  @Override
  public <R> R selectOne(MappedStatement statement, Object... params) throws Exception {
    List<R> result = selectList(statement, params);

    if (result.size() == 1) {
      return result.get(0);
    } else {
      throw new RuntimeException("查询结果为空或者返回结果过多");
    }
  }

  @Override
  public void update(MappedStatement statement, Object... params) throws SQLException {
    SimpleExecutor executor = new SimpleExecutor();
    executor.update(configuration, statement, params);
  }

  @Override
  public void delete(MappedStatement statement, Object... params) throws SQLException {
    SimpleExecutor executor = new SimpleExecutor();
    executor.delete(configuration, statement, params);
  }

  @Override
  public void insert(MappedStatement statement, Object... params) throws SQLException {
    SimpleExecutor executor = new SimpleExecutor();
    executor.insert(configuration, statement, params);
  }



  /**
   * 根据给定的Mapper接口，创建代理对象并返回
   * @param proxyTargetCls  需要创建的Mapper接口字节码对象
   * @param <T>             需要创建的Mapper代理对象类型
   * @return                创建好的Mapper代理对象
   */
  @Override
  public <T> T getMapper(Class<T> proxyTargetCls) {
    Object proxyInstance = Proxy.newProxyInstance(
        this.getClass().getClassLoader(),
        new Class[]{proxyTargetCls},
        (proxy, method, args) -> {

          // 使用反射通过method对象获取statementId
          String methodName = method.getName();
          String className = method.getDeclaringClass().getName();
          String statementId = className + "." + methodName;

          MappedStatement statement = configuration.getStatement(statementId);
          if (statement == null) {
            throw new NoSuchSqlStatementException("未能找到对应的sql配置: " + statementId);
          }

          switch (statement.getType()) {
            case INSERT:
              insert(statement, args);
              return null;
            case UPDATE:
              update(statement, args);
              return null;
            case DELETE:
              delete(statement, args);
              return null;
            case SELECT:
              // 获取method返回类型，判断返回类型来决定执行JDBC的方法
              Class<?> returnType = method.getReturnType();

              if (Collection.class.isAssignableFrom(returnType)) {
                return selectList(statement, args);
              }else if (Map.class.isAssignableFrom(returnType)) {
                throw new NotSupportedException("暂不支持以Map类型返回结果");
              }else {
                return selectOne(statement, args);
              }
            default:
              return null;
          }

        });


    //noinspection unchecked
    return (T) proxyInstance;
  }




}
